//Francisco "Kiko" Olvera
//Camera for DirectX
//Always pointed at center of board
//Can be rotated about center or moved directly above center
//Camera.h

#include "Camera.h"
#include "DirectX.h"
#include "Definitions.h"
#include "Clock.h"
extern int g_CamPos;

void Camera::createCamera(float nearClip, float farClip)
{
	initPoints();
	//Here we specify the field of view, aspect ration and near and far clipping planes.
	D3DXMatrixPerspectiveFovLH(&matProj, D3DXToRadian(65), float(SCREEN_WIDTH)/SCREEN_HEIGHT, nearClip, farClip);
	DirectXFramework::Instance()->getDevice()->SetTransform(D3DTS_PROJECTION, &matProj);

	upVec = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
	moveCamera(SouthBoard);
	currentPos = CAM_SOUTH;
	g_CamPos = currentPos;
	lastPos = CAM_CENTER;
	camMoving = NO_MOVEMENT;
}

void Camera::initPoints()
{
	SouthBoard = D3DXVECTOR3(270.0f, CAM_HEIGHT, -650.0f);
	EastBoard = D3DXVECTOR3(650.0f, CAM_HEIGHT, -270.0f);
	NorthBoard = D3DXVECTOR3(270.0f, CAM_HEIGHT, 110.0f);
	WestBoard = D3DXVECTOR3(-110.0f, CAM_HEIGHT, -270.0f);
	UpBoard = D3DXVECTOR3(270.0f, 710.0f, -270.0f);
	CenterBoard = D3DXVECTOR3(270.0f, 0.0f, -270.0f);
	ViewBoard = D3DXVECTOR3(270.0f, 800.0f, -270.0f);
}

void Camera::pointCamera(D3DXVECTOR3 vec)
{
	lookAt = vec;
	D3DXMatrixLookAtLH(&matView,	&eyePos,	//Camera Position
									&lookAt,	//Look At Position
									&upVec);	//Up Direction
	DirectXFramework::Instance()->getDevice()->SetTransform(D3DTS_VIEW, &matView);
}

void Camera::Update()
{
	g_CamPos = currentPos;
	if(g_CamPos == CAM_CENTER)
		g_CamPos = lastPos;
	float dt = Clock::instance()->getDT();
	dt /= 10;
	switch(camMoving)
	{
	case NO_MOVEMENT:			break;
	case CLOCKWISE_POS:			rotateCameraClock(dt);			break;
	case COUNTERCLOCKWISE_POS:	rotateCameraCounterClock(dt);	break;
	case CLOCKWISE_UP_VEC:		rotateCameraUpVecClock(dt);		break;
	case COUNTERCLOCK_UP_VEC:	rotateCameraUpVecCounterClock(dt);	break;
	case XFORWARD:				rotateCameraXPos(dt);			break;
	case XBACKWARD:				rotateCameraXNeg(dt);			break;
	case ZFORWARD:				rotateCameraZPos(dt);			break;
	case ZBACKWARD:				rotateCameraZNeg(dt);			break;
	case CENTER_UP:				moveCameraUp(dt);				break;
	};
}

//rotates camera around look pos in two dimensions
void Camera::rotateCameraClock(float dt)
{
	theta += dt;

	//                    X = r*sin(theta)                                 Z = r*cos(theta)
	D3DXVECTOR3 tar( (CAM_CIRCLE_RADIUS*sin(D3DXToRadian(theta))), 0.0f, (CAM_CIRCLE_RADIUS*cos(D3DXToRadian(theta))) );

	//translate around center of board
	tar += UpBoard;

	//if distance is close to target, snap to target
	if( (pow((tar.x-target.x), 2) + pow((tar.z-target.z), 2)) <= CAM_SNAP )
	{
		moveCamera(target);
		camMoving = NO_MOVEMENT;
	}
	else
		moveCamera(tar);
}

//rotates camera around look pos in two dimensions
void Camera::rotateCameraCounterClock(float dt)
{
	theta += dt;
	
	//                    X = r*(-sin(theta))                                 Z = r*cos(theta)
	D3DXVECTOR3 tar( -(CAM_CIRCLE_RADIUS*sin(D3DXToRadian(theta))), 0.0f, (CAM_CIRCLE_RADIUS*cos(D3DXToRadian(theta))) );
	
	//translate around center of board
	tar += UpBoard;
	
	//if distance is close to target, snap to target
	if( (pow((tar.x-target.x), 2) + pow((tar.z-target.z), 2)) <= CAM_SNAP )
	{
		moveCamera(target);
		camMoving = NO_MOVEMENT;
	}
	else
		moveCamera(tar);
}

void Camera::rotateCameraXPos(float dt)
{
	theta += dt;

	//									   Y = r*sin(theta)                                 Z = r*cos(theta)
	D3DXVECTOR3 tar( 0.0f, (DISTANCE_FROM_CENTER*sin(D3DXToRadian(theta))), -(DISTANCE_FROM_CENTER*cos(D3DXToRadian(theta))) );

	//translate around center of board
	tar += CenterBoard;

	//if distance is close to target, snap to target
	if( (pow((tar.y-target.y), 2) + pow((tar.z-target.z), 2)) <= CAM_SNAP )
	{
		if(target == UpBoard)
		{
			upVec = D3DXVECTOR3(0.0f, 0.0f, 1.0f);
			moveCamera(target);
			camMoving = CENTER_UP;
			target = ViewBoard;
		}
		else
		{
			upVec = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
			moveCamera(target);
			camMoving = NO_MOVEMENT;
		}
	}
	else
		moveCamera(tar);
}

void Camera::rotateCameraXNeg(float dt)
{
	theta += dt;

	//									   Y = r*sin(theta)                                 Z = r*cos(theta)
	D3DXVECTOR3 tar( 0.0f, (DISTANCE_FROM_CENTER*sin(D3DXToRadian(theta))), (DISTANCE_FROM_CENTER*cos(D3DXToRadian(theta))) );

	//translate around center of board
	tar += CenterBoard;

	//if distance is close to target, snap to target
	if( (pow((tar.y-target.y), 2) + pow((tar.z-target.z), 2)) <= CAM_SNAP )
	{
		if(target == UpBoard)
		{
			upVec = D3DXVECTOR3(0.0f, 0.0f, -1.0f);
			moveCamera(target);
			camMoving = CENTER_UP;
			target = ViewBoard;
		}
		else
		{
			upVec = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
			moveCamera(target);
			camMoving = NO_MOVEMENT;
		}
	}
	else
		moveCamera(tar);
}

void Camera::rotateCameraZPos(float dt)
{
	theta += dt;

	//					X = r*cos(theta)								Y = r*sin(theta)
	D3DXVECTOR3 tar( -(DISTANCE_FROM_CENTER*cos(D3DXToRadian(theta))), (DISTANCE_FROM_CENTER*sin(D3DXToRadian(theta))), 0.0f );

	//translate around center of board
	tar += CenterBoard;

	//if distance is close to target, snap to target
	if( pow((tar.x-target.x), 2) + (pow((tar.y-target.y), 2)) <= CAM_SNAP )
	{
		if(target == UpBoard)
		{
			upVec = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
			moveCamera(target);
			camMoving = CENTER_UP;
			target = ViewBoard;
		}
		else
		{
			upVec = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
			moveCamera(target);
			camMoving = NO_MOVEMENT;
		}
	}
	else
		moveCamera(tar);
}

void Camera::rotateCameraZNeg(float dt)
{
	theta += dt;

	//					X = r*cos(theta)								Y = r*sin(theta)
	D3DXVECTOR3 tar( (DISTANCE_FROM_CENTER*cos(D3DXToRadian(theta))), (DISTANCE_FROM_CENTER*sin(D3DXToRadian(theta))), 0.0f );

	//translate around center of board
	tar += CenterBoard;

	//if distance is close to target, snap to target
	if( pow((tar.x-target.x), 2) + (pow((tar.y-target.y), 2)) <= CAM_SNAP )
	{
		if(target == UpBoard)
		{
			upVec = D3DXVECTOR3(-1.0f, 0.0f, 0.0f);
			moveCamera(target);
			camMoving = CENTER_UP;
			target = ViewBoard;
		}
		else
		{
			upVec = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
			moveCamera(target);
			camMoving = NO_MOVEMENT;
		}
	}
	else
		moveCamera(tar);
}

void Camera::moveCameraUp(float dt)
{
	moveCamera(target);
	camMoving = NO_MOVEMENT;
}

void Camera::rotateCameraUpVecClock(float dt)
{
	theta += dt;
	D3DXVECTOR3 tar( sin(D3DXToRadian(theta)), 0.0f, cos(D3DXToRadian(theta)) );
	if( pow((tar.x-target.x), 2) + (pow((tar.z-target.z), 2)) <= CAM_ROT_SNAP)
	{
		upVec = target;
		camMoving = NO_MOVEMENT;
	}
	else
		upVec = tar;
	moveCamera(ViewBoard);
}

void Camera::rotateCameraUpVecCounterClock(float dt)
{
	theta += dt;
	D3DXVECTOR3 tar( -(sin(D3DXToRadian(theta))), 0.0f, cos(D3DXToRadian(theta)) );
	if( pow((tar.x-target.x), 2) + (pow((tar.z-target.z), 2)) <= CAM_ROT_SNAP)
	{
		upVec = target;
		camMoving = NO_MOVEMENT;
	}
	else
		upVec = tar;
	moveCamera(ViewBoard);
}

void Camera::moveCameraLeft()
{
	camMoving = CLOCKWISE_POS;
	switch(currentPos)
	{
	case CAM_SOUTH:
		{
			theta = 180;
			target = WestBoard;
			currentPos = CAM_WEST;
		}
		break;
	case CAM_WEST:
		{
			theta = 270;
			target = NorthBoard;
			currentPos = CAM_NORTH;
		}
		break;
	case CAM_NORTH:
		{
			theta = 0;
			target = EastBoard;
			currentPos = CAM_EAST;
		}
		break;
	case CAM_EAST:
		{
			theta = 90;
			target = SouthBoard;
			currentPos = CAM_SOUTH;
		}
		break;
	case CAM_CENTER:
		{
			camMoving = CLOCKWISE_UP_VEC;
			switch(lastPos)
			{
			case CAM_SOUTH:	theta = 0;		lastPos = CAM_WEST;		target = D3DXVECTOR3(1.0f, 0.0f, 0.0f);		break;
			case CAM_WEST:	theta = 90;		lastPos = CAM_NORTH;	target = D3DXVECTOR3(0.0f, 0.0f, -1.0f);	break;
			case CAM_NORTH:	theta = 180;	lastPos = CAM_EAST;		target = D3DXVECTOR3(-1.0f, 0.0f, 0.0f);	break;
			case CAM_EAST:	theta = 270;	lastPos = CAM_SOUTH;	target = D3DXVECTOR3(0.0f, 0.0f, 1.0f);		break;
			};
		}
		break;
	};
}

void Camera::moveCameraRight()
{
	camMoving = COUNTERCLOCKWISE_POS;
	switch(currentPos)
	{
	case CAM_SOUTH:
		{
			theta = 180;
			target = EastBoard;
			currentPos = CAM_EAST;
		}
		break;
	case CAM_WEST:
		{
			theta = 90;
			target = SouthBoard;
			currentPos = CAM_SOUTH;
		}
		break;
	case CAM_NORTH:
		{
			theta = 0;
			target = WestBoard;
			currentPos = CAM_WEST;
		}
		break;
	case CAM_EAST:
		{
			theta = 270;
			target = NorthBoard;
			currentPos = CAM_NORTH;
		}
		break;
	case CAM_CENTER:
		{
			camMoving = COUNTERCLOCK_UP_VEC;
			switch(lastPos)
			{
			case CAM_SOUTH:	theta = 0;		lastPos = CAM_EAST;		target = D3DXVECTOR3(-1.0f, 0.0f, 0.0f);	break;
			case CAM_WEST:	theta = 270;	lastPos = CAM_SOUTH;	target = D3DXVECTOR3(0.0f, 0.0f, 1.0f);		break;
			case CAM_NORTH:	theta = 180;	lastPos = CAM_WEST;		target = D3DXVECTOR3(1.0f, 0.0f, 0.0f);		break;
			case CAM_EAST:	theta = 90;		lastPos = CAM_NORTH;	target = D3DXVECTOR3(0.0f, 0.0f, -1.0f);	break;
			};
		}
		break;
	};
}

void Camera::toggleCameraUp()
{
	camMoving = NO_MOVEMENT;
	if(lastPos == CAM_CENTER)
	{
		lastPos = currentPos;
		theta = 60;
		switch(currentPos)
		{
		case CAM_SOUTH:	camMoving = XFORWARD;	break;
		case CAM_WEST:	camMoving = ZFORWARD;	break;
		case CAM_NORTH:	camMoving = XBACKWARD;	break;
		case CAM_EAST:	camMoving = ZBACKWARD;	break;
		};
		target = UpBoard;
		currentPos = CAM_CENTER;
	}
	else
	{
		currentPos = lastPos;
		theta = 90;
		switch(currentPos)
		{
		case CAM_SOUTH:	target = SouthBoard;	camMoving = XBACKWARD;	break;
		case CAM_WEST:	target = WestBoard;		camMoving = ZBACKWARD;	break;
		case CAM_NORTH:	target = NorthBoard;	camMoving = XFORWARD;	break;
		case CAM_EAST:	target = EastBoard;		camMoving = ZFORWARD;	break;
		};
		lastPos = CAM_CENTER;
	}
}